#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.Reflection;
using System.Text;
using System.Globalization;

namespace Yarr
{
	/// <summary>
	/// Provides a tool inspecting objects arbitrary objects as part of the 
	/// debugging process
	/// </summary>
	internal static class Inspector
	{
		/// <summary>
		/// Reflects over all the fields of an object
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
		private static void Fields(Type t, Object o, BindingFlags bindingFlags, StringBuilder stringBuilder) 
		{
            FieldInfo[] fieldInfos = t.GetFields(bindingFlags);

            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                stringBuilder.AppendFormat("\t{0} {1}{2} = {3};\r\n",
                                            fieldInfo.IsPublic ? "public" : "private",
                                            fieldInfo.IsStatic ? "static " : "",
                                            fieldInfo.ToString(), 
                                            fieldInfo.IsStatic ? fieldInfo.GetValue(t) :
                                            o != t ? (fieldInfo.GetValue(o) ?? "null") : "[instance value]");
            }
		}

		/// <summary>
		/// Reflects over all the methods of an object
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
        private static void Methods(Object o, BindingFlags bindingFlags, StringBuilder stringBuilder) 
		{
            if (o != null)
            {
                Type t = (o is Type) ? (Type)o : o.GetType();
                MethodInfo[] methodInfos = t.GetMethods(bindingFlags);

                foreach (MethodInfo methodInfo in methodInfos)
                {
                    stringBuilder.Append("\t");
                    if (methodInfo.IsPublic)
                        stringBuilder.Append("public ");
                    if (methodInfo.IsPrivate)
                        stringBuilder.Append("private ");
                    if (methodInfo.IsAbstract)
                        stringBuilder.Append("abstract ");
                    if (methodInfo.IsStatic)
                        stringBuilder.Append("static ");
                    stringBuilder.AppendFormat("{0}\r\n", methodInfo.ToString());
                }
            }
		}

		/// <summary>
		/// Reflects over all the events for an object
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
        private static void Events(Object o, BindingFlags bindingFlags, StringBuilder stringBuilder) 
		{
            if (o != null)
            {
                Type t = (o is Type) ? (Type)o : o.GetType();
                EventInfo[] eventInfos = t.GetEvents(bindingFlags);

                foreach (EventInfo eventInfo in eventInfos)
                {
                    stringBuilder.Append("\tevent ");
                    if (eventInfo.IsMulticast)
                        stringBuilder.Append("multicast ");
                    stringBuilder.AppendFormat("{0}\r\n", eventInfo.ToString());
                }
            }
		}

		/// <summary>
		/// Reflects over all the properties for an object
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
        private static void Properties(Object o, BindingFlags bindingFlags, StringBuilder stringBuilder) 
		{
            if (o != null)
            {
                Type t = (o is Type) ? (Type)o : o.GetType();
                PropertyInfo[] propertyInfos = t.GetProperties(bindingFlags);

			    foreach (PropertyInfo propertyInfo in propertyInfos) 
			    {
				    stringBuilder.AppendFormat("\tproperty [{0} {1}] {2}\r\n", 
                        propertyInfo.CanRead ? "get" : "*",
                        propertyInfo.CanWrite ? "set" : "*", 
                        propertyInfo.ToString());
			    }
            }
		}

		/// <summary>
		/// Reflects over a type object
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
        private static void TypeInfo(Type type, StringBuilder stringBuilder) 
		{
            if (type != null)
            {
                if (type.IsPublic)
                    stringBuilder.Append("public ");
                if (type.IsAbstract)
                    stringBuilder.Append("abstract ");
                if (type.IsByRef)
                    stringBuilder.Append("byref ");
                if (type.IsSealed)
                    stringBuilder.Append("sealed ");
                if (type.IsImport)
                    stringBuilder.Append("import ");
                if (type.IsClass)
                    stringBuilder.Append("class ");
                if (type.IsArray)
                    stringBuilder.Append("array ");
                if (type.IsCOMObject)
                    stringBuilder.Append("comobject ");
                if (type.IsEnum)
                    stringBuilder.Append("enum ");
                if (type.IsInterface)
                    stringBuilder.Append("interface ");
                if (type.IsPointer)
                    stringBuilder.Append("pointer ");
                stringBuilder.AppendFormat("{0}\r\n", type.FullName);
            }
		}

        private static void EnumValues(Type type, StringBuilder stringBuilder)
        {
            if (type != null  &&  type.IsEnum)
            {
                Type enumType = Enum.GetUnderlyingType(type);
                stringBuilder.Append(String.Format(CultureInfo.CurrentCulture, 
                                                    "\tenum values: {0}\n", enumType));
                foreach (int ix in Enum.GetValues(type))
                    stringBuilder.Append(String.Format(CultureInfo.CurrentCulture, 
                                            "\t  {0} = {1}\n", Enum.GetName(type, ix), ix));
            }
        }

		/// <summary>
		/// Inspects an object by reflecting over its values and meta data
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
		public static string Inspect(Object o) 
		{
			if (o == null) return "null";

            Type t = (o is Type) ? (Type)o : o.GetType();

            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic;
            bindingFlags |= (t == o) ? BindingFlags.Static : BindingFlags.Instance;

			StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.Append(string.Format(CultureInfo.CurrentCulture, "Value := {0}\r\n", o));
			TypeInfo(t, stringBuilder);
            Fields(t, o, bindingFlags, stringBuilder);
            Properties(t, bindingFlags, stringBuilder);
            Events(t, bindingFlags, stringBuilder);
            Methods(t, bindingFlags, stringBuilder);
            if (t.IsEnum)
                EnumValues(t, stringBuilder);

            return stringBuilder.ToString();
		}
	}
}
